/*-------------------------------------------------------------------------
 *
 * spgist.h
 *	  SP-GiST 访问方法的公共头文件。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * src/include/access/spgist.h
 *
 *-------------------------------------------------------------------------
 */
#ifndef SPGIST_H
#define SPGIST_H

#include "access/amapi.h"
#include "access/xlogreader.h"
#include "lib/stringinfo.h"


/* SPGiST操作类支持函数编号 */
#define SPGIST_CONFIG_PROC				1
#define SPGIST_CHOOSE_PROC				2
#define SPGIST_PICKSPLIT_PROC			3
#define SPGIST_INNER_CONSISTENT_PROC	4
#define SPGIST_LEAF_CONSISTENT_PROC		5
#define SPGIST_COMPRESS_PROC			6
#define SPGIST_OPTIONS_PROC				7
#define SPGISTNRequiredProc				5
#define SPGISTNProc						7

/*
 * spg_config方法的参数结构
 */
typedef struct spgConfigIn
{
	Oid			attType;		/* 要被索引的数据类型 */
} spgConfigIn;

typedef struct spgConfigOut
{
	Oid			prefixType;		/* 内部元组前缀的数据类型 */
	Oid			labelType;		/* 内部元组节点标签的数据类型 */
	Oid			leafType;		/* 叶元组值的数据类型 */
	bool		canReturnData;	/* 操作类可以重建原始数据 */
	bool		longValuesOK;	/* 操作类可以处理大于1页的值 */
} spgConfigOut;

/*
 * spg_choose方法的参数结构
 */
typedef struct spgChooseIn
{
	Datum		datum;			/* 要被索引的原始数据项 */
	Datum		leafDatum;		/* 当前要存储在叶子上的数据项 */
	int			level;			/* 当前级别（从零开始计数） */

	/* 当前内部元组的数据 */
	bool		allTheSame;		/* 元组是否标记为相同？ */
	bool		hasPrefix;		/* 元组有前缀吗？ */
	Datum		prefixDatum;	/* 如果有，则是前缀值 */
	int			nNodes;			/* 内部元组中的节点数量 */
	Datum	   *nodeLabels;		/* 节点标签值（如果没有则为NULL） */
} spgChooseIn;

typedef enum spgChooseResultType
{
	spgMatchNode = 1,			/* 下降到现有节点 */
	spgAddNode,					/* 将节点添加到内部元组 */
	spgSplitTuple				/* 分裂内部元组（更改其前缀） */
} spgChooseResultType;

typedef struct spgChooseOut
{
	spgChooseResultType resultType; /* 动作代码，见上文 */
	union
	{
		struct					/* spgMatchNode的结果 */
		{
			int			nodeN;	/* 下降到此节点（索引从0开始） */
			int			levelAdd;	/* 增加这个级别 */
			Datum		restDatum;	/* 新的叶子数据项 */
		}			matchNode;
		struct					/* spgAddNode 的结果 */
		{
			Datum		nodeLabel;	/* 新节点的标签 */
			int			nodeN;	/* 插入的位置（索引从0开始） */
		}			addNode;
		struct					/* spgSplitTuple的结果 */
		{
			/* 以一个子元组构建新的上层内元组的信息 */
			bool		prefixHasPrefix;	/* 元组是否应该有前缀？ */
			Datum		prefixPrefixDatum;	/* 如果是，它的值 */
			int			prefixNNodes;	/* 节点数量 */
			Datum	   *prefixNodeLabels;	/* 它们的标签（如果没有标签则为NULL） */
			int			childNodeN; /* 哪个节点获得子元组 */

			/* 以所有旧节点构建新的下层内元组的信息 */
			bool		postfixHasPrefix;	/* 元组是否应该有前缀？ */
			Datum		postfixPrefixDatum; /* 如果是，它的值 */
		}			splitTuple;
	}			result;
} spgChooseOut;

/*
 * spg_picksplit方法的参数结构
 */
typedef struct spgPickSplitIn
{
	int			nTuples;		/* 叶元组的数量 */
	Datum	   *datums;			/* 它们的数据（长度为nTuples的数组） */
	int			level;			/* 当前级别（从零开始计数） */
} spgPickSplitIn;

typedef struct spgPickSplitOut
{
	bool		hasPrefix;		/* 新内元组是否应该有前缀？ */
	Datum		prefixDatum;	/* 如果是，它的值 */

	int			nNodes;			/* 新内元组的节点数量 */
	Datum	   *nodeLabels;		/* 它们的标签（如果没有标签则为NULL） */

	int		   *mapTuplesToNodes;	/* 每个叶元组的节点索引 */
	Datum	   *leafTupleDatums;	/* 存储在每个新叶元组中的数据 */
} spgPickSplitOut;

/*
 * spg_inner_consistent方法的参数结构
 */
typedef struct spgInnerConsistentIn
{
	ScanKey		scankeys;		/* 运算符和比较值的数组 */
	ScanKey		orderbys;		/* 排序运算符和比较值的数组 */
	int			nkeys;			/* scankeys数组的长度 */
	int			norderbys;		/* orderbys数组的长度 */

	Datum		reconstructedValue; /* 在父节点中重建的值 */
	void	   *traversalValue; /* opclass特定的遍历值 */
	MemoryContext traversalMemoryContext;	/* 在这里放置新的遍历值 */
	int			level;			/* 当前级别（从零开始计数） */
	bool		returnData;		/* 必须返回原始数据吗？ */

	/* 当前内部元组的数据 */
	bool		allTheSame;		/* 元组是否标记为相同？ */
	bool		hasPrefix;		/* 元组有前缀吗？ */
	Datum		prefixDatum;	/* 如果有，则是前缀值 */
	int			nNodes;			/* 内部元组中的节点数量 */
	Datum	   *nodeLabels;		/* 节点标签值（如果没有则为NULL） */
} spgInnerConsistentIn;

typedef struct spgInnerConsistentOut
{
	int			nNodes;			/* 要访问的子节点数量 */
	int		   *nodeNumbers;	/* 它们在节点数组中的索引 */
	int		   *levelAdds;		/* 对每个节点增加这个级别的大小 */
	Datum	   *reconstructedValues;	/* 关联重建值 */
	void	  **traversalValues;	/* 操作类特定遍历值 */
	double	  **distances;		/* 关联距离 */
} spgInnerConsistentOut;

/*
 * spg_leaf_consistent 方法的参数结构
 */
typedef struct spgLeafConsistentIn
{
	ScanKey		scankeys;		/* 运算符和比较值的数组 */
	ScanKey		orderbys;		/* 排序运算符和比较值的数组 */
	int			nkeys;			/* scankeys数组的长度 */
	int			norderbys;		/* orderbys数组的长度 */

	Datum		reconstructedValue; /* 在父节点中重建的值 */
	void	   *traversalValue; /* opclass特定的遍历值 */
	int			level;			/* 当前级别（从零开始计数） */
	bool		returnData;		/* 必须返回原始数据吗？ */

	Datum		leafDatum;		/* 叶元组中的数据 */
} spgLeafConsistentIn;

typedef struct spgLeafConsistentOut
{
	Datum		leafValue;		/* 重建的原始数据（如果有） */
	bool		recheck;		/* 如果操作符必须重新检查，则设置为真 */
	bool		recheckDistances;	/* 如果距离必须重新检查，则设置为真 */
	double	   *distances;		/* 关联距离 */
} spgLeafConsistentOut;


/* spgutils.c */
extern bytea *spgoptions(Datum reloptions, bool validate);

/* spginsert.c */
extern IndexBuildResult *spgbuild(Relation heap, Relation index,
								  struct IndexInfo *indexInfo);
extern void spgbuildempty(Relation index);
extern bool spginsert(Relation index, Datum *values, bool *isnull,
					  ItemPointer ht_ctid, Relation heapRel,
					  IndexUniqueCheck checkUnique,
					  bool indexUnchanged,
					  struct IndexInfo *indexInfo);

/* spgscan.c */
extern IndexScanDesc spgbeginscan(Relation rel, int keysz, int orderbysz);
extern void spgendscan(IndexScanDesc scan);
extern void spgrescan(IndexScanDesc scan, ScanKey scankey, int nscankeys,
					  ScanKey orderbys, int norderbys);
extern int64 spggetbitmap(IndexScanDesc scan, TIDBitmap *tbm);
extern bool spggettuple(IndexScanDesc scan, ScanDirection dir);
extern bool spgcanreturn(Relation index, int attno);

/* spgvacuum.c */
extern IndexBulkDeleteResult *spgbulkdelete(IndexVacuumInfo *info,
											IndexBulkDeleteResult *stats,
											IndexBulkDeleteCallback callback,
											void *callback_state);
extern IndexBulkDeleteResult *spgvacuumcleanup(IndexVacuumInfo *info,
											   IndexBulkDeleteResult *stats);

/* spgvalidate.c */
extern bool spgvalidate(Oid opclassoid);
extern void spgadjustmembers(Oid opfamilyoid,
							 Oid opclassoid,
							 List *operators,
							 List *functions);

#endif							/* SPGIST_H */
